package com.alm.handle.websocket;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ywy
 * @date 2024/8/14
 * 使用springboot的唯一区别是要@Component声明下，而使用独立容器是由容器自己管理websocket的，但在springboot中连容器都是spring管理的。
 * 虽然@Component默认是单例模式的，但springboot还是会为每个websocket连接初始化一个bean，所以可以用一个静态set保存起来。
 */
@Slf4j
@ServerEndpoint("/websocket/{sid}")
@Component
public class WebsocketServer {

    /**
     * 静态变量，用来记录当前在线连接数
     */
    private static int onlineCount = 0;
    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    //private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();

    private static ConcurrentHashMap<String, ArrayList<WebsocketServer>> webSocketServers = new ConcurrentHashMap<>();

    /**
     * 与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    private Session session;

    /**
     * 接收sid
     */
    private String sid;

    /**
     * 建立连接成功的方法
     * @param session
     * @param sid
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid){
        this.session = session;
        ArrayList<WebsocketServer> list=webSocketServers.get(sid);
        if(list==null){
            list = new ArrayList<>();
            webSocketServers.put(sid,list);
        }
        // 线程安全类会引用传递
        list.add(this);
        //在线数加1
        addOnlineCount();

        this.sid=sid;
        try{
//            sendMessage("connect_success");
            log.info("有新窗口开始监听:"+sid+",当前在线人数为" + getOnlineCount());

        }catch (Exception e) {
            e.printStackTrace();
            log.info("websocket IO Exception");
        }
    }

    @OnClose
    public void onClose() {

        if(webSocketServers.get(this.sid) != null) {
            webSocketServers.get(this.sid).remove(this);
            subOnlineCount();
            log.info("有一连接关闭！当前在线人数为" + getOnlineCount());
        }
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        try{
            JSONObject pasing = pasing(message);
            sendInfo(pasing.toJSONString(), this.sid);
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
    public JSONObject pasing(String message){
        try {
            JSONObject jsonObject=JSONObject.parseObject(message);
            JSONObject result = new JSONObject();
            String key=jsonObject.getString("msgType");
            switch (key){
                case "ping":
                    result.put("msgType", "pong");
                    result.put("msgData", "pong");
                    return result;
                case "HeartBeat":
                    result.put("msgType", "ok");
                    result.put("msgData", "ok");
                    return result;
                default:
                    return result;
            }
        }catch (Exception e){
            log.info("数据解析失败 data:{}",message);
        }
        return null;
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message)  {
        try {
            this.session.getBasicRemote().sendText(message);
        }catch (Exception e){
            e.printStackTrace();
            log.info("发送信息失败");
        }

    }


    /**
     * 群发自定义消息
     * */
    public static void sendInfo(String message,@PathParam("sid") String sid) throws IOException {
        ArrayList<WebsocketServer> webSocketServerList =webSocketServers.get(sid);
        if(webSocketServerList!=null){
            for (WebsocketServer webSocketServer:webSocketServerList){
                webSocketServer.sendMessage(message);
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebsocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebsocketServer.onlineCount--;
    }
}
